#region License
// MainViewModel.cs
// 
// Copyright (c) 2012 Xoqal.com
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// 
// http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion

namespace Shammateh.Win.UI.ViewModels
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Collections.Specialized;
    using System.ComponentModel;
    using System.Diagnostics;
    using System.IO;
    using System.Linq;
    using System.Windows.Data;
    using System.Windows.Input;
    using Microsoft.Win32;
    using Shammateh.ImportExport;
    using Shammateh.Win.UI.Controls;
    using Xoqal.Presentation.Commands;
    using Xoqal.Presentation.ViewModels;

    /// <summary>
    /// Main Window View Model.
    /// </summary>
    public class MainViewModel : ViewModelBase
    {
        #region Fields

        private List<NavigationPaneHeader> navigationPaneHeaders;
        private ObservableCollection<IWorkspaceViewModel> workspaces;

        #endregion

        #region Contructors

        /// <summary>
        /// Initializes a new instance of the <see cref="MainViewModel"/> class.
        /// </summary>
        public MainViewModel()
        {
            this.InitializeCommands();
            this.NavigationPaneHeaders = NavigationContainer.GetItems(this.ShowViewCommand);
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the show views command.
        /// </summary>
        /// <value>The show view command.</value>
        public ICommand ShowViewCommand { get; set; }

        /// <summary>
        /// Gets or sets the refresh command.
        /// </summary>
        /// <value>The refresh command.</value>
        public ICommand RefreshCommand { get; set; }

        /// <summary>
        /// Gets or sets the refresh all command.
        /// </summary>
        /// <value>The refresh all command.</value>
        public ICommand RefreshAllCommand { get; set; }

        /// <summary>
        /// Gets or sets the export command.
        /// </summary>
        /// <value>
        /// The export command.
        /// </value>
        public ICommand ExportCommand { get; set; }

        /// <summary>
        /// Gets or sets the import command.
        /// </summary>
        /// <value>
        /// The import command.
        /// </value>
        public ICommand ImportCommand { get; set; }

        /// <summary>
        /// Gets or sets the navigation pane headers.
        /// </summary>
        /// <value>The navigation pane headers.</value>
        public List<NavigationPaneHeader> NavigationPaneHeaders
        {
            get 
            { 
                return this.navigationPaneHeaders; 
            }

            set
            {
                if (value == this.navigationPaneHeaders)
                {
                    return;
                }

                this.navigationPaneHeaders = value;
                this.RaisePropertyChanged(() => this.NavigationPaneHeaders);
            }
        }

        /// <summary>
        /// Returns the collection of available workspaces to display.
        /// A 'workspace' is a ViewModel that can request to be closed.
        /// </summary>
        public ObservableCollection<IWorkspaceViewModel> Workspaces
        {
            get
            {
                if (this.workspaces == null)
                {
                    this.workspaces = new ObservableCollection<IWorkspaceViewModel>();
                    this.workspaces.CollectionChanged += this.OnWorkspacesChanged;
                }

                return this.workspaces;
            }
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Shows the workspace view.
        /// </summary>
        /// <param name="contentViewType">Type of the content view.</param>
        public object ShowWorkspaceView(Type contentViewType)
        {
            IWorkspaceViewModel workspaceViewModel =
                this.Workspaces.FirstOrDefault(v => v.ContentViewModel.GetType() == contentViewType);
            if (workspaceViewModel == null)
            {
                workspaceViewModel = WorkspaceFactory.CreateViewModel(contentViewType);
                this.Workspaces.Add(workspaceViewModel);
            }

            this.SetActiveWorkspace(workspaceViewModel);
            return workspaceViewModel;
        }

        #endregion

        #region Private Methods

        /// <summary>
        /// Initializes the commands.
        /// </summary>
        private void InitializeCommands()
        {
            this.ShowViewCommand = new RelayCommand<Type>(this.OnShowViewCommandExecute);
            this.RefreshCommand = new RelayCommand(this.OnRefreshCommandExecute);
            this.RefreshAllCommand = new RelayCommand(this.OnRefreshAllCommandExecute);
            this.ExportCommand = new RelayCommand<string>(this.OnExportCommandExecute, this.CanExecuteExportCommand);
            this.ImportCommand = new RelayCommand<string>(this.OnImportCommandExecute, this.CanExecuteImportCommand);
        }

        /// <summary>
        /// Called when ShowViewCommand execute.
        /// </summary>
        /// <param name="parameter">The parameter.</param>
        private void OnShowViewCommandExecute(Type parameter)
        {
            this.ShowWorkspaceView(parameter);
        }

        /// <summary>
        /// Called when RefreshCommand execute.
        /// </summary>
        private void OnRefreshCommandExecute()
        {
            ICollectionView collectionView = CollectionViewSource.GetDefaultView(this.Workspaces);
            if (collectionView != null && collectionView.CurrentItem != null &&
                ((IWorkspaceViewModel)collectionView.CurrentItem).ContentViewModel is IRefreshableViewModel)
            {
                this.GetCurrentContentViewModel<IRefreshableViewModel>().RefreshCommand.Execute(null);
            }
        }

        /// <summary>
        /// Called when RefreshAllCommand executed.
        /// </summary>
        private void OnRefreshAllCommandExecute()
        {
            ICollectionView collectionView = CollectionViewSource.GetDefaultView(this.Workspaces);
            foreach (object view in collectionView)
            {
                var refreshableViewModel = ((IWorkspaceViewModel)view).ContentViewModel as IRefreshableViewModel;
                if (refreshableViewModel != null)
                {
                    refreshableViewModel.RefreshCommand.Execute(null);
                }
            }
        }

        /// <summary>
        /// Called when export command execute.
        /// </summary>
        /// <param name="exporterName">Name of the exporter.</param>
        private void OnExportCommandExecute(string exporterName)
        {
            var exporterContainer = this.GetCurrentContentViewModel<IExporterContainer>();
            this.Export(exporterContainer, exporterName);
        }

        /// <summary>
        /// Determines whether this instance can execute export command.
        /// </summary>
        /// <param name="exporterName">Name of the exporter.</param>
        /// <returns>
        ///   <c>true</c> if this instance can execute export command; otherwise, <c>false</c>.
        /// </returns>
        private bool CanExecuteExportCommand(string exporterName)
        {
            var container = this.GetCurrentContentViewModel<IExporterContainer>();
            if (container == null)
            {
                return false;
            }

            return container.CanExport;
        }

        /// <summary>
        /// Exports the specified exporter.
        /// </summary>
        /// <param name="container">The container.</param>
        /// <param name="exporterName">Name of the exporter.</param>
        private void Export(IExporterContainer container, string exporterName)
        {
            IExporter exporter = container.GetExporter(exporterName);

            var dlg = new SaveFileDialog
                      {
                          AddExtension = true,
                          Filter = exporter.FileExtensionFilter
                      };

            if (dlg.ShowDialog() == true)
            {
                using (FileStream stream = File.Create(dlg.FileName))
                {
                    exporter.Export(
                        container.GetExportData(),
                        stream);
                }
            }
        }

        /// <summary>
        /// Called when import command execute.
        /// </summary>
        /// <param name="importerName">Name of the importer.</param>
        private void OnImportCommandExecute(string importerName)
        {
            var importerContainer = this.GetCurrentContentViewModel<IImporterContainer>();
            this.Import(importerContainer, importerName);
        }

        /// <summary>
        /// Determines whether this instance can execute import command.
        /// </summary>
        /// <param name="importerName">Name of the importer.</param>
        /// <returns>
        ///   <c>true</c> if this instance can execute import command; otherwise, <c>false</c>.
        /// </returns>
        private bool CanExecuteImportCommand(string importerName)
        {
            var container = this.GetCurrentContentViewModel<IImporterContainer>();
            if (container == null)
            {
                return false;
            }

            return container.CanImport;
        }

        /// <summary>
        /// Exports the specified importer.
        /// </summary>
        /// <param name="container">The container.</param>
        /// <param name="importerName">Name of the importer.</param>
        private void Import(IImporterContainer container, string importerName)
        {
            IImporter importer = container.GetImporter(importerName);

            var dlg = new OpenFileDialog
                      {
                          Filter = importer.FileExtensionFilter
                      };

            if (dlg.ShowDialog() == true)
            {
                using (FileStream stream = File.OpenRead(dlg.FileName))
                {
                    IEnumerable data = importer.Import(stream);
                    container.SetImportData(data);
                }
            }
        }

        /// <summary>
        /// Gets the current content view model.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        private T GetCurrentContentViewModel<T>()
            where T : class
        {
            ICollectionView collectionView = CollectionViewSource.GetDefaultView(this.Workspaces);
            if (collectionView.CurrentItem == null)
            {
                return null;
            }

            return ((IWorkspaceViewModel)collectionView.CurrentItem).ContentViewModel as T;
        }

        #region Workspaces

        /// <summary>
        /// Called when workspaces changed.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Collections.Specialized.NotifyCollectionChangedEventArgs"/> instance containing the event data.</param>
        private void OnWorkspacesChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.NewItems != null && e.NewItems.Count != 0)
            {
                foreach (IWorkspaceViewModel workspace in e.NewItems)
                {
                    workspace.RequestClose += this.OnWorkspaceRequestClose;
                }
            }

            if (e.OldItems != null && e.OldItems.Count != 0)
            {
                foreach (IWorkspaceViewModel workspace in e.OldItems)
                {
                    workspace.RequestClose -= this.OnWorkspaceRequestClose;
                }
            }
        }

        /// <summary>
        /// Called when workspace request close.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void OnWorkspaceRequestClose(object sender, EventArgs e)
        {
            var workspace = sender as IWorkspaceViewModel;
            if (workspace is IDisposable)
            {
                ((IDisposable)workspace).Dispose();
            }

            this.Workspaces.Remove(workspace);
        }

        /// <summary>
        /// Sets the active workspace.
        /// </summary>
        /// <param name="workspace">The workspace.</param>
        private void SetActiveWorkspace(IWorkspaceViewModel workspace)
        {
            Debug.Assert(this.Workspaces.Contains(workspace), "Work space");

            ICollectionView collectionView = CollectionViewSource.GetDefaultView(this.Workspaces);
            if (collectionView != null)
            {
                collectionView.MoveCurrentTo(workspace);
            }
        }

        #endregion

        #endregion
    }
}
